package com.yt.phone.websocket;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yt.common.constant.Result;
import com.yt.common.enums.CodeEnum;
import com.yt.phone.models.bo.BillSaveBO;
import com.yt.phone.models.bo.HistorySaveBO;
import com.yt.phone.models.bo.PhoneBillSaveBO;
import com.yt.phone.models.dto.BillDTO;
import com.yt.phone.models.dto.PhoneDTO;
import com.yt.phone.models.dto.UserDTO;
import com.yt.phone.models.dto.VirtualphoneDTO;
import com.yt.phone.models.service.*;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.sql.Timestamp;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * WebSocket通话处理类（优化：仅在通话接通后挂断才计费）
 */
@ServerEndpoint("/websocket/call/{phone}")
@Component
public class CallWebSocket {

    private static Map<String, Session> phoneSessionMap = new ConcurrentHashMap<>();
    // 新增：用于记录通话的接通时间，key为通话密钥
    private static Map<String, Long> callAnswerTimeMap = new ConcurrentHashMap<>();
    private static final ObjectMapper objectMapper = new ObjectMapper();
    private String phone;

    private static IHistoryService historyService;
    private static IBillService billService;
    private static IPhoneBillService phoneBillService;
    private static IUserService userService;
    private static IVirtualphoneService virtualphoneService;
    private static IPhoneService phoneService;

    @Resource
    public void setHistoryService(IHistoryService service) {
        historyService = service;
    }
    @Resource
    public void setBillService(IBillService service) {
        billService = service;
    }
    @Resource
    public void setPhoneBillService(IPhoneBillService service) {
        phoneBillService = service;
    }
    @Resource
    public void setUserService(IUserService service) {
        userService = service;
    }
    @Resource
    public void setVirtualphoneService(IVirtualphoneService service) {
        virtualphoneService = service;
    }
    @Resource
    public void setPhoneService(IPhoneService service) {
        phoneService = service;
    }

    @OnOpen
    public void onOpen(Session session, @PathParam("phone") String phone) {
        this.phone = phone;
        phoneSessionMap.put(phone, session);
        System.out.println("手机号 " + phone + " 已连接，当前在线数: " + phoneSessionMap.size());
    }

    @OnClose
    public void onClose() {
        phoneSessionMap.remove(phone);
        System.out.println("手机号 " + phone + " 已断开，当前在线数: " + phoneSessionMap.size());
    }

    @OnMessage
    public void onMessage(String message, Session session) {
        System.out.println("收到来自 " + phone + " 的消息: " + message);

        try {
            JsonNode jsonNode = objectMapper.readTree(message);
            String type = jsonNode.get("type").asText();
            String from = jsonNode.get("from").asText();
            String to = jsonNode.get("to").asText();
            Long userId = jsonNode.get("userId").asLong();
            String userType = jsonNode.get("userType").asText();
            double pay = 0.0;
            if (jsonNode.has("pay") && !jsonNode.get("pay").isNull()) {
                pay = jsonNode.get("pay").asDouble();
            }
            String calltype = jsonNode.get("calltype").asText();
            String callKey = generateCallKey(from, to);
            PhoneDTO phone = new PhoneDTO();
            VirtualphoneDTO virtualphone = new VirtualphoneDTO();
            if ("主叫".equals(calltype)) {
                phone = phoneService.selectOne(new EntityWrapper<PhoneDTO>().eq("phone_number", to));
                virtualphone = virtualphoneService.selectOne(new EntityWrapper<VirtualphoneDTO>().eq("virtualphone_number", from));
            }else if ("被叫".equals(calltype)) {
                phone = phoneService.selectOne(new EntityWrapper<PhoneDTO>().eq("phone_number", from));
                virtualphone = virtualphoneService.selectOne(new EntityWrapper<VirtualphoneDTO>().eq("virtualphone_number", to));
            }else {
                System.out.println("未知类型");
            }


            switch (type) {
                case "call":
                    // 转发呼叫消息，不记录开始时间

                    if (virtualphone.getStatus() == 1&& phone.getStatus() == 1) {
                        sendMessageTo(to, message);
                        virtualphone.setStatus(0);// 呼叫时，将虚拟电话状态置为0，表示呼叫中
                        phone.setStatus(0);
                        virtualphoneService.updateById(virtualphone);
                        phoneService.updateById(phone);

                        break;
                    }
                    else if (virtualphone.getStatus() == 0){
                        System.out.println("虚拟电话 " + from + " 目前不可使用");
                        sendResult(session, new Result<>(CodeEnum.FAIL_100077.getCode(), "虚拟电话 " + from + " 目前不可使用"));
                    } else if (phone.getStatus() == 0) {
                        System.out.println("手机号 " + to + " 目前不可使用");
                        // 发送错误响应给客户端
                        sendResult(session, new Result<>(CodeEnum.FAIL_100076.getCode(), "手机号 " + to + " 目前不可使用"));
                    }

                    break;
                case "answer":
                    // 通话被接听，记录接通时间，并转发接听消息给主叫方
                    callAnswerTimeMap.put(callKey, System.currentTimeMillis());
                    sendMessageTo(to, message);
                    break;
                case "hangup":
                    // 处理挂断逻辑，此时会判断是否需要计费
                    phone.setStatus(1);
                    virtualphone.setStatus(1);
                    virtualphoneService.updateById(virtualphone);
                    phoneService.updateById(phone);

                    handleHangup(from, to, callKey, message, userId, userType, pay, calltype);

                    // 无论是否计费，都向对方发送挂断消息
                    sendMessageTo(from, message);
                    break;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void handleHangup(String from, String to, String callKey, String message, Long userId, String userType, double pay, String calltype) throws IOException {
        // 从接通时间Map中获取时间
        Long answerTime = callAnswerTimeMap.get(callKey);

        // 只有在接通时间存在的情况下，才执行计费等业务逻辑
        if (answerTime != null) {
            // 计算通话时长（从接通到挂断）
            long duration = (System.currentTimeMillis() - answerTime) / 1000;
            System.out.println("通话结束（已接通）（" + from + " -> " + to + "），时长: " + duration + "秒");
            if("被叫".equals(calltype)){
                saveCallHistory(to, from, duration, userId, answerTime, pay);
            }else if("主叫".equals(calltype)){
                saveCallHistory(from, to, duration, userId, answerTime, pay);
            }
            BillDTO bill = saveBill(userId,duration, userType, pay);
            saveBillUser(userId, bill);
            reduceUserBalance(userId, bill.getBill(), userType);

            // 移除通话记录，避免内存泄漏
            callAnswerTimeMap.remove(callKey);
        } else {
            // 如果接通时间不存在，说明通话未被接听就挂断了
            System.out.println("通话结束（未接通）（" + from + " -> " + to + "），不产生费用");
        }

        // 转发挂断消息给被叫方

        sendMessageTo(to, message);
    }

    @OnError
    public void onError(Session session, Throwable error) {
        System.err.println("发生错误");
        error.printStackTrace();
    }

    public static void sendMessageTo(String phone, String message) throws IOException {
        Session session = phoneSessionMap.get(phone);
        if (session != null && session.isOpen()) {
            session.getBasicRemote().sendText(message);
        } else {
            System.err.println("目标手机号 " + phone + " 未连接或会话已关闭");
        }
    }
    private void sendResult(Session session, Result<?> result) throws IOException {
        if (session != null && session.isOpen()) {
            // 将Result对象序列化为JSON字符串
            String jsonResult = objectMapper.writeValueAsString(result);
            session.getBasicRemote().sendText(jsonResult);
        }
    }

    private String generateCallKey(String from, String to) {
        return from.compareTo(to) < 0 ? from + "-" + to : to + "-" + from;
    }

    private BillDTO saveBill(long userId, long duration, String type, double pay) {
        UserDTO user = userService.selectById(userId);

        // 1. 构建 BillDTO（而非 BillSaveBO），因为 BillDTO 是映射数据库表的实体（带 @TableName 和自增主键配置）
        BillDTO billDTO = new BillDTO();

        // 2. 不需要手动设置 billId（数据库自增，MyBatis-Plus 会自动回写）
        // billDTO.setBillId(xxx); // 注释掉手动设置的主键

        // 3. 设置其他必要字段（和之前逻辑一致）
        long calltime = duration / 60 + (duration % 60 > 0 ? 1 : 0);
        billDTO.setBill(calltime * pay);

        if ("公客".equals(type)) {
            billDTO.setStatus(1);
        } else if ("商客".equals(type) && user.getReadyAmount() >= 15) {
            billDTO.setStatus(1);
        } else if ("商客".equals(type) && user.getReadyAmount() < 15) {
            billDTO.setStatus(0);
        }
        BillSaveBO billSaveBO = new BillSaveBO();
        BeanUtils.copyProperties(billDTO, billSaveBO); // 利用Spring的BeanUtils拷贝属性
        billService.add(billSaveBO);
        //查询新加入的账单
        Wrapper<BillDTO> wrapper = new EntityWrapper<>();
        //由大到小最后一个插入的账单
        wrapper.orderBy("bill_id", false);
        billDTO = billService.selectOne(wrapper);

        // 5. 关键：插入后，billDTO 的 billId 已被 MyBatis-Plus 自动回写（数据库生成的自增 ID）
        System.out.println("数据库生成的自增 billId：" + billDTO.getBillId()); // 此时能拿到正确的 ID
        System.out.println("账单信息" + billDTO.toString());

        return billDTO; // 返回 BillDTO，后续用它的 billId
    }

    private void saveBillUser(Long userId, BillDTO bill) {
        PhoneBillSaveBO phoneBill = new PhoneBillSaveBO();
        phoneBill.setUserId(userId);
        phoneBill.setBillId(bill.getBillId());
        phoneBillService.add(phoneBill);
    }

    private void reduceUserBalance(Long userId, double price, String type) {
        UserDTO user = userService.selectById(userId);
            if (user.getReadyAmount() >= 15) {
                user.setReadyAmount(user.getReadyAmount() - price);
                user.setCostAmount(user.getCostAmount() + price);
                userService.updateById(user);
                System.out.println("用户 " + userId + " 扣费 " + price + " 元");
            }
    }

    /**
     * 保存通话历史，并使用接通时间作为通话开始时间
     */
    private void saveCallHistory(String from, String to, long duration, Long userId, Long answerTime, double pay) {

        UserDTO user = userService.selectById(userId);
        HistorySaveBO history = new HistorySaveBO();
        history.setUserId(userId);
        long calltime = duration / 60 + (duration % 60 > 0 ? 1 : 0);
        System.out.println("通话时长：" + calltime);
        System.out.println("通话费用：" + calltime * pay);
        history.setBill(calltime * pay);
        history.setUserName(user.getUserName());
        history.setCallingNumber(from);
        history.setCalledNumber(to);
        history.setTime(duration);

        // 使用接通时间作为通话的开始时间
        history.setCreateTime(new Timestamp(answerTime));
        history.setUpdateTime(new Timestamp(System.currentTimeMillis()));
        history.setStatus(1);

        historyService.add(history);
    }
}